package com.xjgc.exo;

import com.xjgc.msi.exo.wsclient.ClientLoginInterceptor;
import com.xjgc.msi.exo.wsclient.WsClientNotFoundException;
import com.xjgc.msi.exo.wsclient.WsClientStandardRequest;
import com.xjgc.msi.exo.wsclient.WsClientStandardResponse;
import com.xjgc.msi.exo.wsclient.sap.zjk_back02.ZxjZjkBack02Port;
import com.xjgc.msibase.constant.Constant;
import com.xjgc.msibase.constant.StatusEnum;
import com.xjgc.msibase.entity.MsiExoServer;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.apache.cxf.endpoint.Client;
import org.apache.cxf.jaxws.JaxWsProxyFactoryBean;
import org.apache.cxf.jaxws.endpoint.dynamic.JaxWsDynamicClientFactory;
import org.apache.cxf.transport.http.HTTPConduit;
import org.apache.cxf.transports.http.configuration.HTTPClientPolicy;
import org.jeecg.common.exception.JeecgBootException;
import org.jeecg.common.util.SpringContextUtils;

import javax.xml.ws.Holder;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

/**
 * @author: 小逗号
 * @date: 2023-12-25
 * @description:
 * @since:
 */
@Slf4j
public class WsClientStandardManager {





    private void init() {

    }

    /**
     * 设置读超时时间
     *
     * @param client
     * @param readTimeOut
     * @return
     */
    private Client setReadTimeOut(Client client, Long readTimeOut) {
        HTTPConduit conduit = (HTTPConduit) client.getConduit();
        HTTPClientPolicy httpClient = new HTTPClientPolicy();
        httpClient.setConnectionTimeout(1000);
        httpClient.setReceiveTimeout(120000);
        conduit.setClient(httpClient);
        return client;
    }

    /**
     * 设置用户名和密码
     *
     * @param client
     * @param username
     * @param password
     * @return
     */
    private Client setUsernamePassword(Client client, String username, String password) {
        client.getOutFaultInterceptors().add(new ClientLoginInterceptor(username, password));
        return client;
    }

    /**
     * 使用动态wsdl创建客户端
     *
     * @param exoWsServer
     * @return
     */
    private Client createDynamicClient(MsiExoServer exoWsServer) {
        String wsdlUrl = exoWsServer.getWsWsdlUrl();
        JaxWsDynamicClientFactory jaxWsDynamicClientFactory = JaxWsDynamicClientFactory.newInstance();
        log.info("动态客户端wsdl:{}", wsdlUrl);
        Client client = jaxWsDynamicClientFactory.createClient(wsdlUrl);
        //设置超时读时间
        Long readTimeOut = exoWsServer.getReadTimeOut();
        if (readTimeOut != null && !Constant.WS_READ_TIME_OUT.equals(readTimeOut)) {
            setReadTimeOut(client, readTimeOut);
        }
        //设置用户名和密码
        String wsUsername = exoWsServer.getWsUsername();
        String wsPassword = exoWsServer.getWsPassword();
        if (StringUtils.isNotBlank(wsUsername) && StringUtils.isNotBlank(wsPassword)) {
            setUsernamePassword(client, wsUsername, wsPassword);
        }
        return client;
    }

    /**
     * 代理工厂 cxf-client.xml 就是代理工厂,并注册spring
     *
     * @param exoWsServer
     */
    private void proxyFactory(MsiExoServer exoWsServer) {
        // 代理工厂
        /*ClientFactoryBean clientFactoryBean = new ClientFactoryBean();
        JaxWsProxyFactoryBean jaxWsProxyFactoryBean = new JaxWsProxyFactoryBean(clientFactoryBean);*/
        JaxWsProxyFactoryBean jaxWsProxyFactoryBean = new JaxWsProxyFactoryBean();

        // 设置代理地址
        String address = exoWsServer.getWsWsdlUrl();
        jaxWsProxyFactoryBean.setAddress(address);

        //添加用户名密码拦截器
        //jaxWsProxyFactoryBean.getOutFaultInterceptors().add(new ClientLoginInterceptor(username, password));
     /*jaxWsProxyFactoryBean.setWsdlURL("wsdl");
        jaxWsProxyFactoryBean.setUsername("username");
        jaxWsProxyFactoryBean.setPassword("password");*/

        // 设置接口类型
        jaxWsProxyFactoryBean.setServiceClass(ZxjZjkBack02Port.class);
        // 创建一个代理接口实现
        ZxjZjkBack02Port port = (ZxjZjkBack02Port) jaxWsProxyFactoryBean.create();

    }


    /**
     * 创建客户端
     *
     * @param exoWsServer
     * @return
     */
    private Client createClient(MsiExoServer exoWsServer) {
        String wsMethod = exoWsServer.getWsServiceMethod();
        Client client = null;
        if (Constant.WS_CLIENT_METHOD_DYNAMIC_CLIENT.equals(wsMethod)) {
            log.info("使用WS动态客户端");
            client = createDynamicClient(exoWsServer);
        } else if (Constant.WS_CLIENT_METHOD_CLIENT.equals(wsMethod)) {
            log.info("使用客户端");
            //client = createDynamicClient(exoWsServer);
        } else {
            log.info("使用代理工厂,cxf-client.xml 配置的client就是基于代理工厂实现的.");
            log.warn("未知的WS客户端类型!");
        }

        return client;
    }

    public WsClientStandardResponse callWs(MsiExoServer exoWsServer, WsClientStandardRequest request) {
        WsClientStandardResponse response = null;
        String wsClientMethod = exoWsServer.getWsClientMethod();
        if (Constant.WS_CLIENT_METHOD_DYNAMIC_CLIENT.equals(wsClientMethod)) {
            log.info("使用WS动态客户端");
            response = callWsDynamicClient(exoWsServer, request);
        } else if (Constant.WS_CLIENT_METHOD_CLIENT.equals(wsClientMethod)) {
            log.info("使用客户端");
            //client = createDynamicClient(exoWsServer);
        } else if (Constant.WS_CLIENT_METHOD_PROXY_FACTORY.equals(wsClientMethod)) {
            log.info("使用代理工厂");
            response = callWsClientProxy(exoWsServer, request);
        } else {
            log.info("使用代理工厂,cxf-client.xml 配置的client就是基于代理工厂实现的.");
            log.warn("未知的WS客户端类型!");
        }

        return response;
    }

    /**
     * 使用动态代理调用
     *
     * @param exoWsServer
     * @param request
     * @return
     */
    private WsClientStandardResponse callWsDynamicClient(MsiExoServer exoWsServer, WsClientStandardRequest request) {
        WsClientStandardResponse wsClientStandardResponse = null;
        try {
            log.info("根据接口配置的参数列表,组装参数Object[]");
            String wsRequestParams = exoWsServer.getWsRequestParams();
            String[] paramValues = request.getParamValues(wsRequestParams);
            log.info("创建动态客户端");
            Client dynamicClient = createDynamicClient(exoWsServer);
            log.info("使用动态客户端调用WS服务");
            String wsMethod = exoWsServer.getWsServiceMethod();
            Object[] invoke = dynamicClient.invoke(wsMethod, paramValues);
            String wsResponseParams = exoWsServer.getWsResponseParams();
            wsClientStandardResponse = new WsClientStandardResponse(wsResponseParams, invoke);
        } catch (Exception e) {
            e.printStackTrace();
            wsClientStandardResponse = new WsClientStandardResponse();
            wsClientStandardResponse.setState(StatusEnum.STATUS_FAIL.getCode());
            wsClientStandardResponse.setMessage("中间库调用目标WS服务失败!");
            wsClientStandardResponse.setSerialNo(request.getSerialNo());
            wsClientStandardResponse.setExtend(request.getExtend());
        }

        return wsClientStandardResponse;
    }

    /**
     * 调用WebService服务  :使用代理工厂Spring的客户端
     *
     * @param exoWsServer
     * @param request
     * @return
     */
    private WsClientStandardResponse callWsClientProxy(MsiExoServer exoWsServer, WsClientStandardRequest request) {
        log.info("调用WebService服务,使用代理工厂(Spring)");
        WsClientStandard wsClientStandard = getWsClientStandard(exoWsServer);
        WsClientStandardResponse response = null;
        log.info("发送WS请求:{}",request);
        try {
            response = wsClientStandard.callService(request, exoWsServer);
            log.info("WS返回响应:{}",response);
        } catch (Exception e) {
            response = new WsClientStandardResponse();
            response.setState("fail");
            response.setMessage("中间库调用目标WS服务失败!");
            response.setSerialNo(request.getSerialNo());
            response.setExtend(request.getExtend());
        }
        return response;
    }

    /**
     * 客户端代理注册表 , spring声明的客户端
     */
    private final static Map<String, WsClientStandard> CLIENT_PROXY_REGISTRY = new HashMap<>(8);

    private WsClientStandard getWsClientStandard(MsiExoServer exoWsServer) throws WsClientNotFoundException, JeecgBootException {
        String clientId = exoWsServer.getWsClientId();
        log.info("cxf-client:clientId={}", clientId);
        if (!CLIENT_PROXY_REGISTRY.containsKey(clientId)) {
            Object bean = SpringContextUtils.getBean(clientId);
            if (bean == null) {
                throw new WsClientNotFoundException(clientId);
            }
            Class<?> aClass = bean.getClass();
            log.info("clientBean:{}", aClass.getName());
            String serviceMethod = exoWsServer.getWsServiceMethod();
            Method method = null;
            for (Method m : aClass.getMethods()) {
                if (m.getName().equals(serviceMethod)) {
                    method = m;
                    break;
                }
            }
            if (method == null) {
                throw new JeecgBootException("WS客户端配置错误,无效接口方法名!");
            }
            log.info("cxf-client:method={}{}",method.getName(),method.getParameterTypes());
            WsClientStandard wsClientStandard = new WsClientStandard(aClass, method, bean);
            CLIENT_PROXY_REGISTRY.put(clientId, wsClientStandard);
        }
        return CLIENT_PROXY_REGISTRY.get(clientId);
    }

    private class WsClientStandard {
        WsClientStandard(Class<?> aClass, Method method, Object client) {
            this.className = aClass.getName();
            this.aClass = aClass;
            this.method = method;
            this.client = client;
            log.info("代理工厂Spring客户端:{}",className);

        }

        private String className;
        private Class<?> aClass;
        private Method method;
        private Object client;

        WsClientStandardResponse callService(WsClientStandardRequest request, MsiExoServer exoServer) throws InvocationTargetException, IllegalAccessException {
            String[] paramValues = request.getParamValues(exoServer.getWsRequestParams());
            log.info("组装请求参数:{}",paramValues);

            Holder<String> output0 = new Holder<>();
            Holder<String> output1 = new Holder<>();
            Holder<String> output2 = new Holder<>();
            Holder<String> output3 = new Holder<>();
            log.info("响应参数 Holder<String>");
            method.invoke(client, paramValues[0], paramValues[1], paramValues[2], paramValues[3], output0, output1, output2, output3);
            log.info("output0:{}\noutput1:{}\noutput2:{}\noutput3:{}",output0.value,output1.value,output2.value,output3.value);
            log.info("开始进行响应参数封装....");
            WsClientStandardResponse response = new WsClientStandardResponse(exoServer.getWsResponseParams(), new String[]{output0.value, output1.value, output2.value, output3.value});
            return response;
        }
    }
}
